#ifndef __UESDK_HTTP_HTTP_CONNECT_H__
#define __UESDK_HTTP_HTTP_CONNECT_H__

#include "uesdk/config.h"

#ifdef _WIN32
    #include <winsock2.h>
#endif

#include "uesdk/mutex.h"
#include "uesdk/http/http.h"
#include <boost/smart_ptr.hpp>
#include <string>
#include <map>
#include <list>
#include <boost/atomic.hpp>
#include <curl/curl.h>
#include <stdint.h>


namespace uesdk{
namespace http {


class UESDK_DLL_API HttpResult {
public:
    typedef boost::shared_ptr<HttpResult> ptr;

#define ERROR_NAME(XX) \
    XX(0,   OK,                         no error) \
    XX(1,   INVALID_URL,                invalid url) \
    XX(2,   INVALID_HOST,               invalid host) \
    XX(3,   CONNECT_FAIL,               connect faile) \
    XX(4,   SEND_CLOSE_PEER,            The connection is closed by the other party) \
    XX(5,   SEND_SOCKET_ERROR,          Socket error caused by sending request) \
    XX(6,   TIMEOUT,                    timeout) \
    XX(7,   CREAT_SOCKET_ERROR,         create socket error) \
    XX(8,   POOL_GET_CONNECTION,        get connect from pool error) \
    XX(9,   POOL_INVALID_CONNECTION,    Invalid connection)

    enum Error {
        #define XX(num, code, desc) code = num,
            ERROR_NAME(XX)
        #undef XX
    };

    static const char* GetErrorString(Error error){
    #define XX(num,code,desc) \
        if(num == (int)error) {\
            return #desc; \
        }

        ERROR_NAME(XX);
        
    #undef XX    
        return "unknow error";
        
    }

    HttpResult(Error result, HttpResponse::ptr response,const std::string& error);

    Error m_result;
    HttpResponse::ptr m_resonse;
    std::string m_error;
    std::string toString() const;

};

class UESDK_DLL_API HttpConnectionPool;

class UESDK_DLL_API HttpConnection {
friend HttpConnectionPool;
public:
    typedef boost::shared_ptr<HttpConnection> ptr;

    HttpResult::ptr DoGet(const std::string& url, uint64_t timeout_ms
                                , const std::map<std::string, std::string>& header = std::map<std::string, std::string>()
                                , const std::string& body = "");
    HttpResult::ptr DoPost(const std::string& url, uint64_t timeout_ms
                                , const std::map<std::string, std::string>& header = std::map<std::string, std::string>()
                                , const std::string& body = "");
    HttpResult::ptr DoRequest(HttpMethod method, const std::string& url, uint64_t timeout_ms
                                , const std::map<std::string, std::string>& header = std::map<std::string, std::string>()
                                , const std::string& body = "");
    HttpResult::ptr DoRequest(HttpRequest::ptr req, const std::string& url, uint64_t timeout_ms);

    HttpConnection();

    virtual ~HttpConnection();


private:
    uint64_t m_createTime;
    uint64_t m_request;
    bool m_close;
    CURL * m_curl;
};

class UESDK_DLL_API HttpConnectionPool {
public:
    typedef boost::shared_ptr<HttpConnectionPool> ptr;
    typedef Mutex MutexType; 

    /**
     * @brief create a Connect poll
     * 
     * @param uri connect url with http or https, such as https://www.baidu.com
     * @param v_host Virtual host
     * @param max_size max connect size
     * @param max_alive_time Maximum lifetime per connection (ms)
     * @param max_request Maximum number of requests per connection
     * @return HttpConnectionPool::ptr 
     */
    static HttpConnectionPool::ptr Create(const std::string& uri
                                        , const std::string& v_host
                                        , uint32_t max_size = 5
                                        , uint32_t max_alive_time = 120*100
                                        , uint32_t max_request = 10);
    HttpConnectionPool(const std::string& host
                    , const std::string& vhost
                    , uint32_t max_size
                    , uint32_t max_alive_time
                    , uint32_t max_request);
    
    HttpConnection::ptr getConnection();

    HttpResult::ptr DoGet(const std::string& url, uint64_t timeout_ms
                                , const std::map<std::string, std::string>& header = std::map<std::string, std::string>()
                                , const std::string& body = "");
    HttpResult::ptr DoPost(const std::string& url, uint64_t timeout_ms
                                , const std::map<std::string, std::string>& header = std::map<std::string, std::string>()
                                , const std::string& body = "");
    HttpResult::ptr DoRequest(HttpMethod method, const std::string& url, uint64_t timeout_ms
                                , const std::map<std::string, std::string>& header = std::map<std::string, std::string>()
                                , const std::string& body = "");
    HttpResult::ptr DoRequest(HttpRequest::ptr req, const std::string& url, uint64_t timeout_ms);


private:
    static void ReleasePtr(HttpConnection* conn_ptr, HttpConnectionPool* pool);


private:
    std::string m_host;
    std::string m_vhost;
    uint32_t m_maxSize;
    uint32_t m_maxAliveTime;
    uint32_t m_maxRequest;
    
    MutexType m_mutex;
    std::list<HttpConnection*> m_conns;
    boost::atomic<int32_t> m_total;

};

}
}




#endif